%{

/**
******************* INCLUDES CLASSES UTILISATEUR *********************
*/
#include <cstddef>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <stack>
#include <string>
#include "TableId.hpp"
#include "TableSymb.hpp"
#include "Symbole.hpp"
#include "SymboleVar.hpp"
#include "SymboleArg.hpp"
#include "SymboleProg.hpp"
#include "SymboleProcedure.hpp"
#include "SymboleFonction.hpp"
#include "SymboleTemp.hpp"
#include "Type.hpp"
#include "TypeEntier.hpp"
#include "TypeChar.hpp"
#include "TypeReel.hpp"
#include "TypeBool.hpp"
#include "TypeString.hpp"
#include "ConteneurCode.hpp"
#include "Instruction.hpp"
#include "Operande.hpp"
#include "Valeur.hpp"
#include "TypeInterval.hpp"
#include "TypeArray.hpp"
#include "TypeEnum.hpp"
#include "SymboleEtiquette.hpp"
#include "SymboleCst.hpp"
#include "TableSymboleManager.hpp"
#include "actions.hpp"
//*******************************************************//
extern FILE* yyin;
extern int yyparse();
extern int yyerror ( char* );
extern int yylex ();

//******* Variables Globales et tables **********//
TableSymb *table;
extern TableId *tableId;
extern TableSymboleManager tableSM;

int arite = 0; 				/* arite des fonctions et procedures */
Type* typeFunction;			/* type de retour des fonctions  */

unsigned int tempoCourant= 0;		/* notre compteur de temporaire */
Operande* operandeTempo;

/*********compter les branchements************/
int forcourant = 0;
int ifcourant = 0;
int ifelsecourant = 0;
/*********************************************/

/*	declarations listes de variables (pour les arités et les
	positions des arguments*/
vector <int> DeclVarMult;

/*	Les tables de symboles imbriquees */
vector <TableSymb*> listeTDS;

/*	dimensions des tableaux */
vector <TypeInterval*> listInterval;

/* 	gestion des enums */
vector <string> listEnum;

/* la pile pour empiler les tables de symboles */
stack<TableSymb*> stackTDS;

/* 	pour gerer les records par exemple : car on crée en qq sorte
	des tables a la volée */
TableSymb * tableTemp;

/* va contenir le code 3 adresses qu'on va sortir*/
ConteneurCode* codeTroisAdresses = new ConteneurCode();

//************************************************************

using namespace std;

%}

%union {
		Type* typeIdent;
		char* id;
		int numid;
		int valueint;
		float valuefloat;
		bool valuebool;
		char *valuestring;
		Operande* operande;
}

%token KW_PROGRAM
%token KW_CONST
%token KW_TYPE
%token KW_VAR
%token KW_ARRAY
%token KW_OF
%token KW_RECORD
%token KW_BEGIN
%token KW_END
%token KW_DIV
%token KW_MOD
%token KW_AND
%token KW_OR
%token KW_XOR
%token KW_NOT
%token KW_IF
%token KW_THEN
%token KW_ELSE
%token KW_WHILE
%token KW_DO
%token KW_REPEAT
%token KW_UNTIL
%token KW_FOR
%token KW_TO
%token KW_DOWNTO
%token KW_PROC
%token KW_FUNC
%token KW_NIL
%token KW_INTEGER
%token KW_REAL
%token KW_BOOLEAN
%token KW_CHAR
%token KW_STRING

%token SEP_SCOL
%token SEP_DOT
%token SEP_DOTS
%token SEP_DOTDOT
%token SEP_COMMA
%token SEP_CO
%token SEP_CF
%token SEP_PO
%token SEP_PF

%token OP_EQ
%token OP_NEQ
%token OP_LT
%token OP_LTE
%token OP_GT
%token OP_GTE
%token OP_ADD
%token OP_SUB
%token OP_MUL
%token OP_SLASH
%token OP_EXP
%token OP_PTR
%token OP_AFFECT

%token <numid> TOK_IDENT
%token <valueint> TOK_INTEGER
%token <valuefloat> TOK_REAL
%token <valuebool> TOK_BOOLEAN
%token <valuestring> TOK_STRING

%type <typeIdent> Type
%type <typeIdent> BaseType
%type <typeIdent> UserType

/*	on identifie les fonctions, procedures et programme par leur identifiant,
	donc par leur numero dans la table des id. */
%type <numid> ProcIdent
%type <numid> ProcHeader
%type <numid> ProgramHeader
%type <numid> ProcDecl
%type <numid> DeclFunc
%type <numid> ListDeclFunc
%type <numid> BlockDeclFunc
%type <numid> FuncIdent
%type <numid> FuncHeader
%type <numid> FuncDecl
%type <typeIdent> FuncResult

%type <operande> Expression;
%type <operande> BoolExpr;
%type <operande> VarExpr;
%type <operande> AtomExpr;
%type <operande> MathExpr;
%type <operande> CompExpr;

//Gestion des typages particuliers (Records, enums, arrays...)
%type <typeIdent> InterType
%type <typeIdent> ArrayType
%type <typeIdent> ArrayIndex
%type <typeIdent> EnumType
%type <valueint> InterBase
%type <valueint> NSInterBase

%start Program

%nonassoc OP_EQ OP_NEQ OP_GT OP_LT OP_GTE OP_LTE
%left OP_ADD OP_SUB KW_OR KW_XOR
%left OP_MUL OP_SLASH KW_AND KW_DIV KW_MOD
%right KW_NOT OP_NEG OP_POS
%left OP_EXP
%nonassoc OP_PTR
%nonassoc OP_DOT
%left SEP_CO

%nonassoc KW_IFX
%nonassoc KW_ELSE

%%

Program			:	ProgramHeader SEP_SCOL Block SEP_DOT
{
  //table->Ajout(new SymboleProg(), 0);
  //table->Afficher();


  //  tableSM.getCurrentTableSymb()->Ajout(new SymboleProg(), 0);
	cout << endl << endl;
	cout << "** Table Identificateurs" << endl;
	map<const string, const int> tableIdBis;
	map<const int, const string>::const_iterator it;
	for(it = tableId->getData()->begin();
		it != tableId->getData()->end(); it++) {
		tableIdBis.insert(make_pair(it->second, it->first));
	}

	map<const string, const int>::const_iterator itr;
	for(itr = tableIdBis.begin(); itr != tableIdBis.end(); ++itr)
	{
	    cout << itr->first << " "
			<< itr->second << endl;
	}

	//tableId->print();
	cout << endl << endl;
	tableSM.print();
	cout << endl << endl;
	codeTroisAdresses->affichage();
}
				;

ProgramHeader	:	KW_PROGRAM TOK_IDENT
{
	// initialisation d'une nouvelle table des symboles pour la remonter
	// a la regle Program
	table= new TableSymb(tableId->getId($2), NULL);
	tableSM.beginProgram(table);
	tableSM.getCurrentTableSymb()->Ajout(new SymboleProg(), 0);

	//listeTDS.push_back(table);
	//stackTDS.push(table);
	$$=$2;
	// génération du code 3 adresses correspondant a la declaration du
	// programme
	Instruction *debInstruction = new Instruction(tableId->getId($2), "DEB");
	codeTroisAdresses->ajouterInstruct(debInstruction);
}
				;

Block			:	BlockDeclConst BlockDeclType BlockDeclVar
								BlockDeclFunc BlockCode
				;

BlockBis		:	BlockDeclConst BlockDeclType BlockDeclVar BlockDeclFunc BlockCode
				;

BlockDeclConst	:	KW_CONST ListDeclConst
			 	|
			 	;

ListDeclConst	:	ListDeclConst DeclConst
			 	|	DeclConst
			 	;

DeclConst		:	TOK_IDENT OP_EQ Expression SEP_SCOL
			 	;

BlockDeclType	:	KW_TYPE ListDeclType
			 	|
			 	;

ListDeclType	:	ListDeclType DeclType
			 	|	DeclType
			 	;

DeclType		:	TOK_IDENT OP_EQ Type SEP_SCOL
			 	;

BlockDeclVar	:	KW_VAR ListDeclVar
			 	|
			 	;

ListDeclVar		:	ListDeclVar DeclVar
			 	|	DeclVar
			 	;

DeclVar			:	ListIdent SEP_DOTS BaseType SEP_SCOL
{
	for (unsigned int i=0;i<DeclVarMult.size(); i++)
	{
		Symbole *temp = new SymboleVar($3);
		tableSM.getCurrentTableSymb()->Ajout(temp, DeclVarMult[i]);
    }
    DeclVarMult.clear();
}
				;

ListIdent		:	ListIdent SEP_COMMA TOK_IDENT
{
	DeclVarMult.push_back($3);
	arite++;
}
			 	|	TOK_IDENT
{
	DeclVarMult.push_back($1);
	arite++;
}
			 	;

BlockDeclFunc	:	ListDeclFunc SEP_SCOL
{
     Instruction *debInstruction =
			new Instruction(tableId->getId($1), "DEB");
     codeTroisAdresses->ajouterInstruct(debInstruction);
     $$ = $1;
}
			 	|
			 	;

ListDeclFunc	:	ListDeclFunc SEP_SCOL DeclFunc {$$=$3;}
			 	|	DeclFunc {$$=$1;}
			 	;

DeclFunc		:	{arite=0;} ProcDecl
{
  TableSymb* ts = tableSM.getCurrentTableSymb();
  ts->Ajout(new SymboleProcedure(arite-1), $2);
  $$=$2;
}

			 	|	{arite=0;} FuncDecl
{
  // Modifier l'arité ?
  $$=$2;
}
			 	;

ProcDecl		:	ProcHeader SEP_SCOL BlockBis {$$=$1;}
			 	;

ProcHeader		:	ProcIdent {$$=$1;}
			 	|	ProcIdent FormalArgs {$$=$1;}
			 	;

ProcIdent		:	KW_PROC TOK_IDENT
{
  $$=$2;
  tableSM.begin(tableId->getId($2));
}
			 	;

FormalArgs		:	SEP_PO ListFormalArgs SEP_PF
			 	;

ListFormalArgs	:	ListFormalArgs SEP_SCOL FormalArg
			 	|	FormalArg
			 	;

FormalArg		:	ValFormalArg
			 	|	VarFormalArg
			 	;

ValFormalArg	:	ListIdent SEP_DOTS BaseType
			 	;

VarFormalArg	:	KW_VAR ListIdent SEP_DOTS BaseType
			 	;

FuncDecl		:	FuncHeader SEP_SCOL BlockBis  {$$=$1;}
			 	;

FuncHeader		:	FuncIdent FuncResult
{
	typeFunction = $2;
	TableSymb* ts = tableSM.getCurrentTableSymb();
	ts->Ajout(new SymboleFonction($2,0), $1);
	tableSM.begin(tableId->getId($1));
	$$=$1;
}
			 	|	FuncIdent FormalArgs FuncResult
{
	typeFunction = $3;
	TableSymb* ts = tableSM.getCurrentTableSymb();
	ts->Ajout(new SymboleFonction($3,arite), $1);
	tableSM.begin(tableId->getId($1));
	$$=$1;
}
			 	;

FuncIdent		:	KW_FUNC TOK_IDENT
{
  $$=$2;// tableSM.begin(tableId->getId($2));
}
			 	;

FuncResult		:	SEP_DOTS BaseType {$$ = $2;}
			 	;

Type			:	UserType	{$$ = $1;}
			 	|	BaseType	{$$ = $1;}
			 	;

UserType		:	EnumType   	{$$ = $1;}
			 	|	InterType 	{$$ = $1;}
			 	|	ArrayType  	{$$ = $1;}
			 	|	RecordType
			 	|	PointerType
			 	;

BaseType		:	TOK_IDENT 	{$$=NULL;}
			 	|	KW_INTEGER 	{$$ = new TypeEntier();}
				|	KW_REAL 	{$$ = new TypeReel();}
				|	KW_BOOLEAN 	{$$ = new TypeBool();}
				|	KW_CHAR 	{$$ = new TypeChar();}
				|	KW_STRING 	{$$ = new TypeString();}
				;

EnumType		:	SEP_PO ListEnumValue SEP_PF
{
	TypeEnum *tempType;

	for (unsigned int i = 0; i < listEnum.size(); i++)
	{
    	tempType = new TypeEnum(listEnum);
    	Symbole *temp = new SymboleCst(tempType,listEnum.at(i));
    	table->Ajout(temp, tableId->getNum(listEnum.at(i)));
	}
	$$ = new TypeEnum(listEnum);
	listEnum.clear();
}
			 	;

ListEnumValue	:	ListEnumValue SEP_COMMA TOK_IDENT
{
	listEnum.push_back(tableId->getId($3));
}
			 	|	TOK_IDENT
{
	listEnum.push_back(tableId->getId($1));
}
			 	;

InterType		:	InterBase SEP_DOTDOT InterBase
			 	;

InterBase		:	NSInterBase
			 	|	OP_SUB NSInterBase
			 	;

NSInterBase		:	TOK_IDENT
			 	|	TOK_INTEGER
			 	;

ArrayType		:	KW_ARRAY SEP_CO ListArrayIndex SEP_CF KW_OF BaseType
 			 	;

ListArrayIndex	:	ListArrayIndex SEP_COMMA ArrayIndex
				|	ArrayIndex
				;

ArrayIndex		:	TOK_IDENT
			 	|	TOK_INTEGER
			 	;

RecordType		:	KW_RECORD RecordFields KW_END
			 	;

RecordFields	:	RecordFields SEP_SCOL RecordField
			 	|	RecordField
			 	;

RecordField		:	ListIdent SEP_DOTS BaseType
			 	;

PointerType		:	OP_PTR BaseType
			 	;

BlockCode		:	KW_BEGIN ListInstr KW_END
				|	KW_BEGIN ListInstr SEP_SCOL KW_END
				|	KW_BEGIN KW_END
			 	;

ListInstr		:	ListInstr SEP_SCOL Instr
			 	|	Instr
			 	;

Instr			:	KW_WHILE Expression KW_DO Instr
			 	|	KW_REPEAT ListInstr KW_UNTIL Expression
			 	|	KW_FOR TOK_IDENT OP_AFFECT Expression ForDirection
							Expression KW_DO Instr
			 	|	KW_IF Expression KW_THEN Instr %prec KW_IFX
			 	|	KW_IF Expression KW_THEN Instr KW_ELSE Instr
			 	|	VarExpr OP_AFFECT Expression
{
	//cout << "Debug : Valeur (Instr) : "
	//	<< $3->getValeur()->toString() << endl;
	Instruction* nouvelleInstruction = new Instruction (
			"CPY", *$1, *$3, *tableId);
	codeTroisAdresses->ajouterInstruct(nouvelleInstruction);
}
			 	|	Call
			 	|	BlockCode
			 	;

ForDirection	:	KW_TO
			 	|	KW_DOWNTO
			 	;

Expression		:	MathExpr
			 	|	CompExpr
			 	|	BoolExpr
			 	|	AtomExpr
			 	|	VarExpr
				|	Call
			 	;

MathExpr		:	Expression OP_ADD Expression
{
	actionMath(*codeTroisAdresses, "ADD", *$$, *$1, *$3);
}
			 	|	Expression OP_SUB Expression
{
	actionMath(*codeTroisAdresses, "SUB", *$$, *$1, *$3);
}

			 	|	Expression OP_MUL Expression
{
	actionMath(*codeTroisAdresses, "MUL", *$$, *$1, *$3);
}

			 	|	Expression OP_SLASH Expression
{
	actionMath(*codeTroisAdresses, "DIV", *$$, *$1, *$3);
}

			 	|	Expression KW_DIV Expression
{
	// Div entiere.
	// TODO
	//actionMath(*codeTroisAdresses, "DIVE", *$$, *$1, *$3);
}

			 	|	Expression KW_MOD Expression
{
	actionMath(*codeTroisAdresses, "MOD", *$$, *$1, *$3);
}

			 	|	Expression OP_EXP Expression
			 	|	OP_SUB Expression %prec OP_NEG
			 	|	OP_ADD Expression %prec OP_POS
			 	;

CompExpr		:	Expression OP_EQ Expression
{
	actionMath(*codeTroisAdresses, "EQ", *$$, *$1, *$3);
}

			 	|	Expression OP_NEQ Expression
{
	actionMath(*codeTroisAdresses, "NEQ", *$$, *$1, *$3);
}

			 	|	Expression OP_LT Expression
{
	actionMath(*codeTroisAdresses, "LT", *$$, *$1, *$3);
}

			 	|	Expression OP_LTE Expression
{
	actionMath(*codeTroisAdresses, "LTE", *$$, *$1, *$3);
}

			 	|	Expression OP_GT Expression
{
	actionMath(*codeTroisAdresses, "GT", *$$, *$1, *$3);
}

			 	|	Expression OP_GTE Expression
{
	actionMath(*codeTroisAdresses, "GTE", *$$, *$1, *$3);
}

			 	;

BoolExpr		:	Expression KW_AND Expression
{
	actionBool(*codeTroisAdresses, "AND", *$$, *$1, *$3);
}
			 	|	Expression KW_OR Expression
{
	actionBool(*codeTroisAdresses, "OR", *$$, *$1, *$3);
}
			 	|	Expression KW_XOR Expression
{
	actionBool(*codeTroisAdresses, "XOR", *$$, *$1, *$3);
}
			 	|	KW_NOT Expression
{
	actionBool(*codeTroisAdresses, "NOT", *$$, *$2);
}
			 	;

AtomExpr		:	SEP_PO Expression SEP_PF
{
	$$ = $2;
}
			 	|	TOK_INTEGER
{
	// -1 qui correspond au fait que l'entier n'a pas de id sur la TI.
	Valeur *v = new Valeur(new TypeEntier, $1);
	//cout << "Debug : Valeur creation = " << v->toString() << endl;
	$$ = new Operande(-1, v, true);
}
			 	|	TOK_REAL
{
	Valeur *v = new Valeur(new TypeReel, $1);
	//cout << "Debug : Valeur creation = " << v->toString() << endl;
	$$ = new Operande(-1, v, true);
}
			 	|	TOK_BOOLEAN
{
	$$ = new Operande(-1, new Valeur(new TypeBool, $1), true);
}
			 	|	KW_NIL

			 	|	TOK_STRING
{
	string *strVal = new string($1);
	Valeur *v = new Valeur(new TypeString, strVal);
	//cout << "Debug : Valeur creation = " << v->toString() << endl;
	$$ = new Operande(-1, v, true);
}
			 	;

VarExpr			:	TOK_IDENT
{
  //tableSM.print();
  Symbole* s= tableSM.find($1);
  if(s==NULL)
    {
      cout << "VarExpr : table->getSymbole($1) == NULL" << endl;
      /*tableSM.getCurrentTableSymb()->Ajout(new SymboleVar(),$1);
	s= tableSM.find($1); */
    }else{
  $$ = new Operande(
		    $1, new Valeur(s->getType()), false);
  }
}
				|	TOK_IDENT SEP_CO ListIndices SEP_CF
				|	TOK_IDENT SEP_DOT TOK_IDENT %prec OP_DOT
				|	TOK_IDENT OP_PTR
				;

Call			:	TOK_IDENT Parameters
				;

Parameters		:	SEP_PO ListParameters SEP_PF
				|	SEP_PO SEP_PF
				;

ListIndices		:	ListIndices SEP_COMMA Expression
				|	Expression
				;

ListParameters	:	ListParameters SEP_COMMA Expression
				|	Expression
				;

%%
